/**
 * 文件名: packaged_task_usage_test.cpp
 *
 * 构建:
 *   g++ -std=c++20 -pthread packaged_task_usage_test.cpp \
 *       -lgtest -lgtest_main -o packaged_task_usage_test
 * 运行:
 *   ./packaged_task_usage_test
 *
 * 示例覆盖:
 *   1. 基础用法：任务在线程中执行，future 获取结果。
 *   2. 异常传播：任务抛出异常，future.get() 捕获。
 *   3. 一次性执行限制：重复调用 operator() 抛出 future_error。
 *   4. 移动语义：将任务移动至容器/线程执行。
 *   5. 简易任务队列：演示在线程池风格环境下调度 packaged_task。
 */

#include <gtest/gtest.h>

#include <condition_variable>
#include <future>
#include <mutex>
#include <queue>
#include <thread>

using namespace std::chrono_literals;

// -----------------------------------------------------------------------------
// 1) 基础用法：将 Lambda 封装为 packaged_task，在独立线程执行。
// -----------------------------------------------------------------------------
TEST(PackagedTaskUsageTest, RunsCallableOnSeparateThread) {
    // 定义任务类型：接受 int，返回 int。
    std::packaged_task<int(int)> task([](int x) {
        std::this_thread::sleep_for(5ms);
        return x * x;
    });

    // get_future() 只能调用一次，返回与任务绑定的 future。
    std::future<int> fut = task.get_future();

    // 将任务移动到线程对象中执行。注意：task 是一次性资源，必须 std::move。
    std::thread worker(std::move(task), 5);
    worker.join();

    // future.get() 阻塞等待并获取任务结果。
    EXPECT_EQ(fut.get(), 25);
}

// -----------------------------------------------------------------------------
// 2) 异常传播：任务抛出异常，在 future.get() 中捕获。
// -----------------------------------------------------------------------------
TEST(PackagedTaskUsageTest, PropagatesExceptionToFuture) {
    std::packaged_task<void()> task([] {
        throw std::runtime_error("failure");
    });

    auto fut = task.get_future();

    std::thread worker(std::move(task));
    worker.join();

    EXPECT_THROW(fut.get(), std::runtime_error);
}

// -----------------------------------------------------------------------------
// 3) 一次性执行：重复调用 operator() 会抛 future_error。
// -----------------------------------------------------------------------------
TEST(PackagedTaskUsageTest, TaskCanRunOnlyOnce) {
    std::packaged_task<int()> task([] { return 1; });
    auto fut = task.get_future();

    task();  // 第一次执行成功
    EXPECT_EQ(fut.get(), 1);

    // 再次执行会抛出异常 (std::future_error with future_errc::promise_already_satisfied)
    EXPECT_THROW(task(), std::future_error);
}

// -----------------------------------------------------------------------------
// 4) 移动语义：将 packaged_task 放入容器后提取执行。
// -----------------------------------------------------------------------------
TEST(PackagedTaskUsageTest, SupportsMoveSemanticsForScheduling) {
    using TaskType = std::packaged_task<int()>;
    std::queue<TaskType> queue;

    TaskType task([] { return 123; });
    auto fut = task.get_future();

    // moves into queue
    queue.push(std::move(task));
    EXPECT_TRUE(queue.size() == 1);

    // 取出任务并执行
    TaskType next = std::move(queue.front());
    queue.pop();
    next();  // execute

    EXPECT_EQ(fut.get(), 123);

    // 任务执行后已无效，再次执行会抛 future_error
    EXPECT_THROW(next(), std::future_error);
}

// -----------------------------------------------------------------------------
// 5) 简易任务队列：模拟线程池从队列取出 packaged_task。
//    展示如何在生产者-消费者模型中使用 packaged_task + future。
// -----------------------------------------------------------------------------
class SimpleTaskQueue {
public:
    ~SimpleTaskQueue() {
        Stop();
    }

    // 启动单个工作线程
    void Start() {
        worker_ = std::thread([this] { WorkerLoop(); });
    }

    // 提交任务：返回 future，用于获取异步结果
    template <class Fn, class... Args>
    auto Submit(Fn&& fn, Args&&... args) -> std::future<std::invoke_result_t<Fn, Args...>> {
        using ResultType = std::invoke_result_t<Fn, Args...>;

        // 组装 packaged_task，将可调用对象与参数绑定
        std::packaged_task<ResultType()> task(
            std::bind(std::forward<Fn>(fn), std::forward<Args>(args)...));

        auto fut = task.get_future();

        {
            std::lock_guard<std::mutex> lock(mutex_);
            tasks_.emplace(std::move(task));
        }
        cv_.notify_one();
        return fut;
    }

    // 停止队列，等待线程退出
    void Stop() {
        {
            std::lock_guard<std::mutex> lock(mutex_);
            stopped_ = true;
        }
        cv_.notify_all();
        if (worker_.joinable()) {
            worker_.join();
        }
    }

private:
    using TaskType = std::packaged_task<void()>;

    void WorkerLoop() {
        while (true) {
            TaskType task;
            {
                std::unique_lock<std::mutex> lock(mutex_);
                cv_.wait(lock, [this] {
                    return stopped_ || !tasks_.empty();
                });

                if (stopped_ && tasks_.empty()) {
                    break;
                }

                task = std::move(tasks_.front());
                tasks_.pop();
            }
            task();  // 执行任务
        }
    }

    std::thread worker_;
    std::mutex mutex_;
    std::condition_variable cv_;
    std::queue<TaskType> tasks_;
    bool stopped_ = false;
};

TEST(PackagedTaskUsageTest, WorksInSimpleTaskQueue) {
    SimpleTaskQueue queue;
    queue.Start();

    auto fut1 = queue.Submit([](int x) {
        std::this_thread::sleep_for(5ms);
        return x * 2;
    }, 21);

    auto fut2 = queue.Submit([](std::string text) {
        return text + " world";
    }, std::string("hello"));

    EXPECT_EQ(fut1.get(), 42);
    EXPECT_EQ(fut2.get(), "hello world");

    queue.Stop();
}